#include <cppunit/config/SourcePrefix.h>

#include "PKBStubForFollowTest.h"
#include "PKBStubForParentTest.h"
#include "QueryTable.h"
#include "QueryExpressionParser.h"
#include "QueryEvaluator.h"
#include "TestQueryEvaluator.h"

void QueryEvaluatorTest::setUp() {
	cout << "QueryEvaluatorTest setUp()\n";
}

void QueryEvaluatorTest::tearDown() {
	cout << "QueryEvaluatorTest tearDown()\n";
}

// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( QueryEvaluatorTest );

// method to test the constructor
void QueryEvaluatorTest::testConstructor() {
	cout << "QueryEvaluatorTest testConstructor()\n";
	// Just to ensure it does not produce errors
	PKB pkb; // PKB needed for query evaluator constructor
	QueryEvaluator evaluator(&pkb);
}

void QueryEvaluatorTest::testEvaluateFollows() {
	cout << "Test evaluate follows\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = y;
	//2.	y = z;
	//3.	a = b;
	//4.	b = c;
	// }
	PKBStubForFollowTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	// CASE IN WHICH THE QUERY SYNONYM HAS TWO UNDERSCORE ARGUMENTS
	// Make a query table from: "Stmt s; Select s"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("s");
	// Evaluate it!
	cout << "COME HERE!!!\n";
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(4, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM HAS TWO UNDERSCORE ARGUMENTS
	// Make a query table from: "Assign a; Stmt s; Select a such that Follows(_, _)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "_", SYNONYM_PROGLINE, "_", SYNONYM_PROGLINE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(4, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM DOES NOT MATCH ANY ARGUMENT
	// Make a query table from: "Assign a; Stmt s; Select a such that Follows(1, s)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "1", INTEGER, "s", SYNONYM_STMT);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(4, (int)results.size());

	// Make a query table from: "Assign a; Select a such that Follows(1, 3)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "1", INTEGER, "3", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());
	
	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "Assign a; Select a such that Follows(a, 2)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "a", SYNONYM_ASSIGN, "2", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(1, (int)results.size());
	CPPUNIT_ASSERT_EQUAL(std::string("1"), results.at(0));

	// Make a query table from: "Assign a; Select a such that Follows(a, 1)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "a", SYNONYM_ASSIGN, "1", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE RIGHT ARGUMENT
	// Make a query table from: "Assign a; Stmt s; Select a such that Follows(s, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "s", SYNONYM_STMT, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// Make a query table from: "Assign a; Select a such that Follows(4, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "4", INTEGER, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES BOTH ARGUMENTS
	// Make a query table from: "Assign a; Select a such that Follows(a, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "a", SYNONYM_ASSIGN, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());
}

void QueryEvaluatorTest::testEvaluateFollowsStar() {
	cout << "Test evaluate follows star\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = y;
	//2.	y = z;
	//3.	a = b;
	//4.	b = c;
	// }
	PKBStubForFollowTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	// CASE IN WHICH THE QUERY SYNONYM HAS TWO UNDERSCORE ARGUMENTS
	// Make a query table from: "Assign a; Stmt s; Select a such that Follows*(_, _)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "_", SYNONYM_PROGLINE, "_", SYNONYM_PROGLINE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(4, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES NONE OF THE ARGUMENTS
	// Make a query table from: "Assign a; Stmt s; Select a such that Follows*(s, 2)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "s", SYNONYM_STMT, "2", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(4, (int)results.size());

	// Make a query table from: "Assign a; Stmt s; Select a such that Follows*(s, 1)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "s", SYNONYM_ASSIGN, "1", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "Assign a; Select a such that Follows*(a, 3)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "a", SYNONYM_ASSIGN, "3", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());

	// Make a query table from: "Assign a; Select a such that Follows*(a, 1)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "a", SYNONYM_ASSIGN, "1", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE RIGHT ARGUMENT
	// Make a query table from: "Assign a; Select a such that Follows*(1, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "1", INTEGER, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// Make a query table from: "Assign a; Select a such that Follows*(4, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "4", INTEGER, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES BOTH ARGUMENTS
	// Make a query table from: "Assign a; Select a such that Follows*(a, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "a", SYNONYM_ASSIGN, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());
}

void QueryEvaluatorTest::testEvaluateParent() {
	cout << "Test evaluate parent\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = 2;
	//2.	y = x;
	//3.	while x {
	//4.		x = x - 1;
	//5.		y = y + x;
	//6.		while y {
	//7.			z = y - 1;
	//			}
	//		}
	//8.	y = 5;
	// }
	PKBStubForParentTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	// stmt s; Select s such that Parent(s,4)
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("s");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "s", SYNONYM_STMT, "4", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(1, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM HAS TWO UNDERSCORE ARGUMENTS
	// Make a query table from: "Stmt s; Select s such that Parent(_, _)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("s");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "_", SYNONYM_PROGLINE, "_", SYNONYM_PROGLINE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(8, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES NONE OF THE ARGUMENTS
	// Make a query table from: "assign a; while w; Select w such that Parent(w, 4)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "4", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(1, (int)results.size());

	// Make a query table from: "assign a; stmt s; Select a such that Parent(s, 8)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(ASSIGN, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "s", SYNONYM_STMT, "8", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "while w; stmt s; Select w such that Parent(w, 4)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "4", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(1, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "while w; Select w such that Parent(w, 4)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "8", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE RIGHT ARGUMENT
	// Make a query table from: "while w; assign a; Select a such that Parent(w, a)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "a", SYNONYM_ASSIGN);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// Make a query table from: "while w; Select w such that Parent(1, w)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "1", INTEGER, "w", SYNONYM_WHILE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES BOTH ARGUMENTS
	// Make a query table from: "while w; Select w such that Parent(w, w)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "w", SYNONYM_WHILE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());
}

void QueryEvaluatorTest::testEvaluateParentStar() {
	cout << "Test evaluate parent star\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = 2;
	//2.	y = x;
	//3.	while x {
	//4.		x = x - 1;
	//5.		y = y + x;
	//6.		while y {
	//7.			z = y - 1;
	//			}
	//		}
	//8.	y = 5;
	// }
	PKBStubForParentTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	// CASE IN WHICH THE QUERY SYNONYM HAS TWO UNDERSCORE ARGUMENTS
	// Make a query table from: "Assign a; Stmt s; Select a such that Parent*(_, _)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "_", SYNONYM_PROGLINE, "_", SYNONYM_PROGLINE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(6, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM DOES NOT MATCH ANY ARGUMENT
	// Make a query table from: "while w; Select w such that Parent*(3, 7)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "3", INTEGER, "7", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());

	// Make a query table from: "while w; Select w such that Parent*(1,8)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "1", INTEGER, "8", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "stmt s; Select s such that Parent*(s, 7)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("s");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "s", SYNONYM_WHILE, "7", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());

	// Make a query table from: "while w; Select w such that Parent*(w, 8)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "w", SYNONYM_WHILE, "8", INTEGER);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE RIGHT ARGUMENT
	// Make a query table from: "while w; stmt s; Select w such that Parent*(s, w)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "s", SYNONYM_STMT, "w", SYNONYM_WHILE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(1, (int)results.size());

	// Make a query table from: "while w; Select w such that Parent*(2, w)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "2", INTEGER, "w", SYNONYM_WHILE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES BOTH ARGUMENTS
	// Make a query table from: "while w; Select w such that Parent*(w, w)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "w", SYNONYM_WHILE, "w", SYNONYM_WHILE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());
}

void QueryEvaluatorTest::testEvaluateModifies() {
	cout << "Test evaluate modifies\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = 2;
	//2.	y = x;
	//3.	while x {
	//4.		x = x - 1;
	//5.		y = y + x;
	//6.		while y {
	//7.			z = y - 1;
	//			}
	//		}
	//8.	y = 5;
	// }
	PKBStubForParentTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	// CASE IN WHICH BOTH ARGUMENTS ARE UNDERSCORE
	// Make a query table from: "variable v; Select v such that Modifies("_","_");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mResultTable.addSynonym("v");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "_", SYNONYM_PROGLINE, "_", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM DOES NOT MATCH ANY ARGUMENT
	// Make a query table from: "assign a; Select a such that Modifies(1,"x");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "1", INTEGER, "x", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(6, (int)results.size());

	// Make a query table from: "assign a; stmt s; variable v; Select a such that Modifies(s,v);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "s", SYNONYM_STMT, "v", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(6, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "assign a; Select a such that Modifies(a,"x");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "a", SYNONYM_ASSIGN, "x", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());

	// Make a query table from: "prog_line p; Select p such that Modifies(p,"z");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(PROG_LINE, "p");
	qTable.mResultTable.addSynonym("p");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "p", SYNONYM_PROGLINE, "z", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE RIGHT ARGUMENT
	// Make a query table from: "assign a; variable v; Select v such that Modifies(a,v);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mResultTable.addSynonym("v");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "a", SYNONYM_ASSIGN, "v", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// Make a query table from: "while w; variable v; Select v such that Modifies(w,v);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mResultTable.addSynonym("v");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "w", SYNONYM_WHILE, "v", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());
}

void QueryEvaluatorTest::testEvaluateUses() {
	cout << "Test evaluate uses\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = 2;
	//2.	y = x;
	//3.	while x {
	//4.		x = x - 1;
	//5.		y = y + x;
	//6.		while y {
	//7.			z = y - 1;
	//			}
	//		}
	//8.	y = 5;
	// }
	PKBStubForParentTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	// CASE IN WHICH BOTH ARGUMENTS ARE UNDERSCORE
	// Make a query table from: "variable v; Select v such that Uses(_,_);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mResultTable.addSynonym("v");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "_", SYNONYM_PROGLINE, "_", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM DOES NOT MATCH ANY ARGUMENT
	// Make a query table from: "assign a; Select a such that Uses(2,"x");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "2", INTEGER, "x", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(6, (int)results.size());

	// Make a query table from: "constant c; Select c such that Uses(2,"x");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(CONSTANT, "c");
	qTable.mResultTable.addSynonym("c");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "2", INTEGER, "x", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// Make a query table from: "assign x; Select a such that Uses(1,"x");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "x");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "1", INTEGER, "x", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE LEFT ARGUMENT
	// Make a query table from: "variable v; stmt s; Select s such that Uses(s,v);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("s");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "s", SYNONYM_STMT, "v", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(6, (int)results.size());
	
	// Make a query table from: "stmt s; Select s such that Uses(s,"z");"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("s");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "s", SYNONYM_STMT, "z", STRING);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// CASE IN WHICH THE QUERY SYNONYM MATCHES THE RIGHT ARGUMENT
	// Make a query table from: "variable v; stmt s; Select v such that Uses(s,v);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(STMT, "s");
	qTable.mResultTable.addSynonym("v");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "s", SYNONYM_STMT, "v", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());

	// Make a query table from: "variable v; Select v such that Uses(1,v);"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mResultTable.addSynonym("v");
	qTable.mRelClauseTable.addRelationshipEntry(USES_LINE, "1", INTEGER, "v", SYNONYM_VARIABLE);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());
}

// Currently only tests assign patterns like Pattern a("x","5") without suchthat-clauses
void QueryEvaluatorTest::testEvaluatePatternAssign() {
	cout << "Test evaluate pattern + assign\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = 2;
	//2.	y = x;
	//3.	while x {
	//4.		x = x - 1;
	//5.		y = y + x;
	//6.		while y {
	//7.			z = y - 1;
	//			}
	//		}
	//8.	y = 5;
	// }
	PKBStubForParentTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryExpressionParser *queryExpParser = new QueryExpressionParser(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	TreeNode *arg2 = NULL;
	TreeNode *arg3 = NULL;
	
	// EXISTING PATTERN IN WHICH THERE IS A LEFT AND THERE IS A RIGHT ARGUMENT (NO UNDERSCORE)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("y", "5")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = queryExpParser->convertExpressionToTree("5");
	arg3 = NULL;
	CPPUNIT_ASSERT(arg2 != NULL);
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "y", true, arg2, true, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// NON-EXISTING PATTERN IN WHICH THERE IS A LEFT AND THERE IS A RIGHT ARGUMENT (NO UNDERSCORE)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("y", "x+x")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = queryExpParser->convertExpressionToTree("x+x");
	arg3 = NULL;
	CPPUNIT_ASSERT(arg2 != NULL);
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "y", true, arg2, true, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// EXISTING PATTERN IN WHICH THERE IS ONLY LEFT ARGUMENT (UNDERSCORE RIGHT)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("y", "_")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "y", true, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// NON-EXISTING PATTERN IN WHICH THERE IS ONLY LEFT ARGUMENT (UNDERSCORE RIGHT)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("b", "_")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "b", true, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// EXISTING PATTERN IN WHICH THERE IS ONLY RIGHT ARGUMENT (UNDERSCORE LEFT)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("_", "y-1")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = queryExpParser->convertExpressionToTree("y-1");
	arg3 = NULL;
	CPPUNIT_ASSERT(arg2 != NULL);
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "_", false, arg2, true, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());

	// NON-EXISTING PATTERN IN WHICH THERE IS ONLY RIGHT ARGUMENT (UNDERSCORE LEFT)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("_", "y")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = queryExpParser->convertExpressionToTree("y");
	arg3 = NULL;
	CPPUNIT_ASSERT(arg2 != NULL);
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "_", false, arg2, true, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// EXISTING PATTERN IN WHICH THERE IS NO ARGUMENT (UNDERSCORE LEFT AND RIGHT)
	// Make a query table from: "variable v; assign a; Select v Pattern a ("_", "_")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(VARIABLE, "v");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("v");
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "_", false, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());
}

// Tests the evaluateQuery function for both such that and PATTERNS!
// Tests queries which have both a suchthat-clause and a pattern-clause
void QueryEvaluatorTest::testEvaluateSuchThatPatternAssign() {
	cout << "Test evaluate suchthat + pattern + assign\n";
	// Currently assumes the following SIMPLE source code:
	// Procedure MyProc {
	//1.	x = 2;
	//2.	y = x;
	//3.	while x {
	//4.		x = x - 1;
	//5.		y = y + x;
	//6.		while y {
	//7.			z = y - 1;
	//			}
	//		}
	//8.	y = 5;
	// }
	PKBStubForParentTest pkbStub;
	QueryEvaluator evaluator(&pkbStub);
	QueryExpressionParser *queryExpParser = new QueryExpressionParser(&pkbStub);
	QueryTable qTable;
	std::vector<std::string> results;

	TreeNode *arg2 = NULL;
	TreeNode *arg3 = NULL;
	
	// Make a query table from: "while w; assign a; Select a such that Follows*(w,a) pattern a("y",_)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS_STAR, "w", SYNONYM_WHILE, "a", SYNONYM_ASSIGN);
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "y", true, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(1, (int)results.size());
	CPPUNIT_ASSERT_EQUAL(std::string("8"), results.at(0));

	// Make a query table from: "assign a1, a2; Select a1 such that Follows(a1,a2) pattern a2(_,_)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a1");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a2");
	qTable.mResultTable.addSynonym("a1");
	qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "a1", SYNONYM_ASSIGN, "a2", SYNONYM_ASSIGN);
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a2", "_", false, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());
	CPPUNIT_ASSERT_EQUAL(std::string("1"), results.at(0));
	CPPUNIT_ASSERT_EQUAL(std::string("4"), results.at(1));

	// Make a query table from: "assign a; while w; Select a such that Parent(w,a) pattern a(_,_)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "a", SYNONYM_ASSIGN);
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "_", false, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());
	CPPUNIT_ASSERT_EQUAL(std::string("4"), results.at(0));
	CPPUNIT_ASSERT_EQUAL(std::string("5"), results.at(1));
	CPPUNIT_ASSERT_EQUAL(std::string("7"), results.at(2));

	// Make a query table from: "assign a; while w; Select w such that Parent*(w,a) pattern a(_,"y-1")"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("w");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT_STAR, "w", SYNONYM_WHILE, "a", SYNONYM_ASSIGN);
	arg2 = queryExpParser->convertExpressionToTree("y-1");
	arg3 = NULL;
	CPPUNIT_ASSERT(arg2 != NULL);
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "_", false, arg2, true, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(2, (int)results.size());
	CPPUNIT_ASSERT_EQUAL(std::string("3"), results.at(0));
	CPPUNIT_ASSERT_EQUAL(std::string("6"), results.at(1));

	// Make a query table from: "assign a; Select a such that Modifies(a,"x") pattern a("y",_)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(MODIFIES_LINE, "a", SYNONYM_ASSIGN, "x", STRING);
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "y", true, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(0, (int)results.size());

	// Make a query table from: "assign a; while w; Select a such that Parent(w,a) pattern a(_,_)"
	qTable = QueryTable();
	qTable.mSynonymTable.addSynonym(WHILE, "w");
	qTable.mSynonymTable.addSynonym(ASSIGN, "a");
	qTable.mResultTable.addSynonym("a");
	qTable.mRelClauseTable.addRelationshipEntry(PARENT, "w", SYNONYM_WHILE, "a", SYNONYM_ASSIGN);
	arg2 = NULL;
	arg3 = NULL;
	qTable.mPatternTable.addPattern(PATTERN_ASSIGN, "a", "_", false, arg2, false, arg3, false);
	// Evaluate it!
	results = evaluator.evaluateQuery(qTable);
	CPPUNIT_ASSERT_EQUAL(3, (int)results.size());
	CPPUNIT_ASSERT_EQUAL(std::string("4"), results.at(0));
	CPPUNIT_ASSERT_EQUAL(std::string("5"), results.at(1));
	CPPUNIT_ASSERT_EQUAL(std::string("7"), results.at(2));

	// THE FOLLOWING IS TESTABLE ONLY WHEN PKBSTUB CAN ACCEPT PARTIAL MATCHING

	//// Make a query table from: "assign a1, a2; Select a2 such that Follows(a1,a2) pattern a2(_,_"y"_)"
	//qTable = QueryTable();
	//qTable.mSynonymTable.addSynonym(ASSIGN, "a1");
	//qTable.mSynonymTable.addSynonym(ASSIGN, "a2");
	//qTable.mResultTable.addSynonym("a2");
	//qTable.mRelClauseTable.addRelationshipEntry(FOLLOWS, "a1", SYNONYM_ASSIGN, "a2", SYNONYM_ASSIGN);
	//arg2 = queryExpParser->convertExpressionToTree("y");
	//arg3 = NULL;
	//qTable.mPatternTable.addPattern("a2", "_", false, arg2, false, arg3, false);
	//// Evaluate it!
	//results = evaluator.evaluateQuery(qTable);
	//CPPUNIT_ASSERT_EQUAL(1, (int)results.size());
	//CPPUNIT_ASSERT_EQUAL(std::string("5"), results.at(0));
}